/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         https://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkMembershipSample_hxx
#define itkMembershipSample_hxx

namespace itk::Statistics
{
template <typename TSample>
void
MembershipSample<TSample>::SetNumberOfClasses(unsigned int numberOfClasses)
{
  m_NumberOfClasses = numberOfClasses;
  m_ClassSamples.resize(m_NumberOfClasses);
  for (unsigned int i = 0; i < m_NumberOfClasses; ++i)
  {
    m_ClassSamples[i] = ClassSampleType::New();
    (m_ClassSamples[i])->SetSample(this->GetSample());
  }
}

template <typename TSample>
inline void
MembershipSample<TSample>::AddInstance(const ClassLabelType & classLabel, const InstanceIdentifier & id)
{
  m_ClassLabelHolder[id] = classLabel;
  int classIndex = this->GetInternalClassLabel(classLabel);
  if (classIndex == -1)
  {
    m_UniqueClassLabels.push_back(classLabel);
    classIndex = static_cast<int>(m_UniqueClassLabels.size()) - 1;
  }

  (m_ClassSamples[classIndex])->AddInstance(id);
}

template <typename TSample>
inline unsigned int
MembershipSample<TSample>::GetClassLabel(const InstanceIdentifier & id) const
{
  return (*(m_ClassLabelHolder.find(id))).second;
}

template <typename TSample>
inline int
MembershipSample<TSample>::GetInternalClassLabel(const ClassLabelType classLabel) const
{
  for (unsigned int i = 0; i < m_UniqueClassLabels.size(); ++i)
  {
    if (m_UniqueClassLabels[i] == classLabel)
    {
      return i;
    }
  }

  return -1;
}

template <typename TSample>
auto
MembershipSample<TSample>::GetClassLabelHolder() const -> const ClassLabelHolderType
{
  return m_ClassLabelHolder;
}

template <typename TSample>
auto
MembershipSample<TSample>::GetClassSample(const ClassLabelType & classLabel) const -> const ClassSampleType *
{
  const int classIndex = this->GetInternalClassLabel(classLabel);
  if (classIndex < 0)
  {
    return nullptr;
  }

  return m_ClassSamples[classIndex];
}

template <typename TSample>
inline const typename MembershipSample<TSample>::MeasurementVectorType &
MembershipSample<TSample>::GetMeasurementVector(const InstanceIdentifier & id) const
{
  return m_Sample->GetMeasurementVector(id);
}

template <typename TSample>
inline auto
MembershipSample<TSample>::GetMeasurement(const InstanceIdentifier & id, const unsigned int dimension)
  -> MeasurementType
{
  return m_Sample->GetMeasurement(id, dimension);
}

template <typename TSample>
inline auto
MembershipSample<TSample>::GetFrequency(const InstanceIdentifier & id) const -> AbsoluteFrequencyType
{
  return m_Sample->GetFrequency(id);
}

template <typename TSample>
inline auto
MembershipSample<TSample>::GetTotalFrequency() const -> TotalAbsoluteFrequencyType
{
  return m_Sample->GetTotalFrequency();
}

template <typename TSample>
void
MembershipSample<TSample>::Graft(const DataObject * thatObject)
{
  this->Superclass::Graft(thatObject);

  // Most of what follows is really a deep copy, rather than grafting of
  // output. Wish it were managed by pointers to bulk data. Sigh !

  const auto * thatConst = dynamic_cast<const Self *>(thatObject);
  if (thatConst)
  {
    auto * that = const_cast<Self *>(thatConst);
    this->m_UniqueClassLabels = that->m_UniqueClassLabels;
    this->m_ClassLabelHolder = that->m_ClassLabelHolder;
    this->m_ClassSamples = that->m_ClassSamples;
    this->m_Sample = that->m_Sample;
    this->m_NumberOfClasses = that->m_NumberOfClasses;
  }
}

template <typename TSample>
void
MembershipSample<TSample>::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);
  os << indent << "Sample: " << m_Sample.GetPointer() << std::endl;
  os << indent << "NumberOfClasses: " << this->GetNumberOfClasses() << std::endl;
}
} // namespace itk::Statistics

#endif
